﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MGenScope.Properties;
using System.IO;
using System.Diagnostics;

namespace MGenScope
{
    public partial class BaseForm : Form,ILog
    {
        EngineDriver engineTrusted;
        EngineDriver engineInTest;
        public int Depth { get; set; }
        public string Fen { get; set; }
        public BaseForm()
        {
            Depth = Settings.Default.Depth;
            Fen = Settings.Default.Fen;
            engineInTest = Settings.Default.InTest;
            if( null == engineInTest )
                engineInTest = new EngineDriver();
            engineTrusted = Settings.Default.Trusted;
            if( null == engineTrusted )
                engineTrusted = new EngineDriver();
            engineInTest.Log = engineTrusted.Log = this;
            engineInTest.Complete += new EventHandler(engineInTest_Complete);
            engineTrusted.Complete += new EventHandler(engineTrusted_Complete);
            InitializeComponent();
            linkLabelChengine.Links.Add(new LinkLabel.Link(0, linkLabelChengine.Text.Length, "www.codeplex.com/chengine"));
            linkLabelFelix.Links.Add(new LinkLabel.Link(0, linkLabelFelix.Text.Length, "mailto://felice@felicepollano.com"));
            textBoxInTest.DataBindings.Add(new Binding("Text",engineInTest,"ExePath"));
            textBoxTrusted.DataBindings.Add(new Binding("Text", engineTrusted, "ExePath"));
            textBoxDepth.DataBindings.Add(new Binding("Text", this, "Depth"));
            textBoxFen.DataBindings.Add(new Binding("Text", this, "Fen"));
            textBoxInTest.Tag = buttonSettingsInTest.Tag = buttonInTest.Tag = engineInTest;
            textBoxTrusted.Tag = buttonSettingsTrusted.Tag = buttonTrusted.Tag = engineTrusted;
        }
        int currentDepth;
        void engineTrusted_Complete(object sender, EventArgs e)
        {
            engineInTest.Run();
            
            if( null == lastMove )
                engineInTest.Divide(engineTrusted.Depth, engineTrusted.CurrentFen);
            else
                engineInTest.Divide(engineTrusted.Depth, engineTrusted.FenBeforeMove,lastMove);
        }

        Queue<DivideResults> WrongQueue = new Queue<DivideResults>();

        void engineInTest_Complete(object sender, EventArgs e)
        {
            IDictionary<string, DivideResults> trusted = engineTrusted.Results;
            IDictionary<string, DivideResults> test = engineInTest.Results;
            BeginCollectErrors();
            if (FenEqual(engineInTest.CurrentFen,engineTrusted.CurrentFen))
            {
                foreach (KeyValuePair<string, DivideResults> pair in trusted)
                {
                   
                    if (test.ContainsKey(pair.Key))
                    {
                        pair.Value.Checked = true;
                        pair.Value.Ok = true;
                        test[pair.Key].Checked = true;
                        if (pair.Value.Count == test[pair.Key].Count)
                            test[pair.Key].Ok = true;
                    }
                    else
                        pair.Value.Ok = false;
                }
                foreach (KeyValuePair<string, DivideResults> pair in trusted)
                {
                    if (pair.Value.Checked == false)
                        ReportMissingMove(pair.Value);
                }
                foreach (KeyValuePair<string, DivideResults> pair in test)
                {
                    if (pair.Value.Checked == false)
                    {
                        ReportInvalidMove(pair.Value);
                    }
                    else
                        if (pair.Value.Ok == false)
                        {
                            if( WrongQueue.Count < 1 )
                                WrongQueue.Enqueue(pair.Value);
                        }
                }
            }
            else
            {
                // Fen error
                ReporFenError(lastFen, lastMove, engineTrusted.CurrentFen, engineInTest.CurrentFen);
            }
            FlushErrorReport();
            if (WrongQueue.Count == 0)
            {
                ReportDone();
               
            }
            else
            {
                RepeatTest();
            }
        }
        /// <summary>
        /// Check fen equality without 50 moves draw part.
        /// </summary>
        /// <param name="fen1"></param>
        /// <param name="fen2"></param>
        /// <returns></returns>
        private bool FenEqual(string fen1, string fen2)
        {
            string[] tokens1 = fen1.Split(' ');
            string[] tokens2 = fen2.Split(' ');
            if (tokens1.Length < 4 && tokens2.Length < 4)
                throw new Exception("Invalid fen string");
            for (int i = 0; i < 4; ++i)
            {
                if (string.Compare(tokens1[i], tokens2[i]) != 0)
                    return false;
            }
            return true;
        }

        string lastMove;
        string lastFen;
        
        private void RepeatTest()
        {
            DivideResults res = WrongQueue.Dequeue();
            currentDepth = res.Depth - 1;
            lastMove = res.Move;
            lastFen = res.Fen;
            engineTrusted.Divide(currentDepth, res.Fen, res.Move);
        }

        private void ReportDone()
        {
            Invoke(new MethodInvoker(delegate()
            {
                if (mMistakes.Count == 0 &&
                    fMistakes.Count == 0)
                    labelDone.BackColor = Color.Lime;
                else
                    labelDone.BackColor = Color.Red;
                labelDone.Visible = true;
                buttonRun.Enabled = true;
                buttonCancel.Enabled = false;
            }));
        }
        IList<MoveMistake> mMistakes;
        IList<FenMistake> fMistakes;
        MoveMistake mMistake;
        FenMistake fMistake;
        private void BeginCollectErrors()
        {
            Invoke(new MethodInvoker(delegate()
            {
                mMistake = null;
                fMistake = null;
            }));
        }
        private void ReporFenError(string lastFen, string lastMove, string trustedFen, string testFen)
        {
            Invoke(new MethodInvoker(delegate()
            {
                if (null == fMistake)
                    fMistake = new FenMistake();
                fMistake.ExpectedFen = trustedFen;
                fMistake.OriginFen = lastFen;
                fMistake.WrongFen = testFen;
                fMistake.Move = lastMove;
            }));
        }
        private void ReportInvalidMove(DivideResults divideResults)
        {
            Invoke(new MethodInvoker(delegate()
            {
                if (null == mMistake)
                {
                    mMistake = new MoveMistake();
                    mMistake.ListInvalid = "";
                    mMistake.Fen = divideResults.Fen;
                }
                mMistake.ListInvalid += divideResults.Move;
                mMistake.ListInvalid += ";";
            }));
        }
        private void ReportMissingMove(DivideResults divideResults)
        {
            Invoke(new MethodInvoker(delegate()
            {
                if (null == mMistake)
                {
                    mMistake = new MoveMistake();
                    mMistake.ListMissing = "";
                    mMistake.Fen = divideResults.Fen;
                }
                mMistake.ListMissing += divideResults.Move;
                mMistake.ListMissing += ";";
            }));
               
        }
        private void FlushErrorReport()
        {
            Invoke(new MethodInvoker(delegate()
            {
                if (null != mMistake)
                    mMistakes.Add(mMistake);
                if (null != fMistake)
                    fMistakes.Add(fMistake);
                moveMistakeBindingSource.ResetBindings(true);
                fenMistakeBindingSource.ResetBindings(true);
                
            }));
        }
        private void BrowseClick(object sender, EventArgs e)
        {
            EngineDriver driver = (sender as Control).Tag as EngineDriver;
            OpenFileDialog fd = new OpenFileDialog();
            fd.Filter = "Exe Files (*.exe)|*.exe|All files (*.*)|*.*";
            fd.Title = "Select the engine";
            if (fd.ShowDialog() == DialogResult.OK)
            {
                driver.ExePath = fd.FileName;
            }
            
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            engineInTest.Kill();
            engineTrusted.Kill();
            Settings.Default["Trusted"] = engineTrusted;
            Settings.Default["InTest"] = engineInTest;
            Settings.Default.Save();
        }

        private void buttonSettings_Click(object sender, EventArgs e)
        {
            EngineDriver driver = (sender as Control).Tag as EngineDriver;
            EngineSettingsForm form = new EngineSettingsForm();
            form.Settings = driver.EngineSettings.Clone() as EngineSettings;
            if (DialogResult.OK == form.ShowDialog())
                driver.EngineSettings = form.Settings;
        }

        private void buttonRun_Click(object sender, EventArgs e)
        {
            lastMove = null;
            lastFen = null;
            labelDone.Visible = false;
            currentDepth = Depth;
            buttonRun.Enabled = false;
            engineTrusted.Run();
            buttonCancel.Enabled = true;
            engineTrusted.Divide(Depth,Fen);
            mMistakes = new List<MoveMistake>();
            fMistakes = new List<FenMistake>();
            fenMistakeBindingSource.DataSource = fMistakes;
            moveMistakeBindingSource.DataSource = mMistakes;
        }

        private void buttonCancel_Click(object sender, EventArgs e)
        {
            engineTrusted.Kill();
            engineInTest.Kill();
            buttonCancel.Enabled = false;
            buttonRun.Enabled = true;
        }

        private void EngineChanged(object sender, EventArgs e)
        {
            EngineDriver driver = (sender as Control).Tag as EngineDriver;
            driver.EngineSettings.WorkingFolder = Path.GetDirectoryName((sender as TextBox).Text);
        }

        #region ILog Members

        public void LogEngineMessage(string s)
        {
            Invoke(new MethodInvoker(delegate() 
                {
                    richTextBox1.AppendText(s);
                    richTextBox1.AppendText("\n");
                    richTextBox1.ScrollToCaret();
                    Application.DoEvents(); 
                }));
        }
        

        #endregion

        private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Process.Start(e.Link.LinkData.ToString());
        }
    }
}
